/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ClassDemo.h"
#include "hilog/log.h"
#define BUFF_SIZE   1024

person::person(uint32_t age, std::string name) : age_(age), name_(name)
{
    OH_LOG_INFO(LOG_APP, "create person :%{public}s %{public}d", name_.c_str(), age_);
}

napi_value person::SayAge(napi_env env, napi_callback_info info)
{
    napi_value res;
    napi_status status;
    napi_value jsthis;
    status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
    person *obj;
    status = napi_unwrap(env, jsthis, (void**)&obj);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_unwrap failed! status=%{public}d", status);
        return nullptr;
    }
    
    status = napi_create_int32(env, obj->age_, &res);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_int32 failed! status=%{public}d", status);
        return nullptr;
    }
    
    return res;
}

napi_value person::SayName(napi_env env, napi_callback_info info) {
    napi_value res;
    napi_status status;
    napi_value jsthis;
    status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
    person *obj;
    status = napi_unwrap(env, jsthis, (void **)&obj);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_unwrap failed! status=%{public}d", status);
        return nullptr;
    }

    status = napi_create_string_utf8(env, obj->name_.c_str(), NAPI_AUTO_LENGTH, &res);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_string_utf8 failed! status=%{public}d", status);
        return nullptr;
    }

    return res;
}

napi_value person::SetName(napi_env env, napi_callback_info info)
{
    napi_value res = nullptr;
    napi_status status;
    napi_value jsthis;
    napi_value argv[1];
    size_t argc = 1;
    
    status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_cb_info failed! status=%{public}d", status);
        return nullptr;
    }
    
    person *obj;
    status = napi_unwrap(env, jsthis, (void**)&obj);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_unwrap failed! status=%{public}d", status);
        return nullptr;
    }
    
    char buf[BUFF_SIZE] = {0};
    size_t len = 0;
    status = napi_get_value_string_utf8(env, argv[0], buf, BUFF_SIZE, &len);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_value_string_utf8 failed! status=%{public}d", status);
        return nullptr;
    }

    obj->name_ = std::string(buf);
    
    return res;
}

napi_value person::GetName(napi_env env, napi_callback_info info) {
    napi_value res = nullptr;
    napi_status status;
    napi_value jsthis;
    napi_value argv[1];
    size_t argc = 1;

    status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_cb_info failed! status=%{public}d", status);
        return nullptr;
    }

    person *obj;
    status = napi_unwrap(env, jsthis, (void **)&obj);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_unwrap failed! status=%{public}d", status);
        return nullptr;
    }
    status = napi_create_string_utf8(env, obj->name_.c_str(), obj->name_.size(), &res);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_string_utf8 failed! status=%{public}d", status);
        return nullptr;
    }

    return res;
}

napi_value person::Constructor(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value argv[2] = {nullptr};
    napi_value jsthis = nullptr;
    
    napi_status status;
    status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_cb_info failed! status=%{public}d", status);
        return nullptr;
    }
    
    int32_t age;
    char buf[BUFF_SIZE] = {0};
    size_t len = 0;
    // 获取年龄参数
    status = napi_get_value_int32(env, argv[0], &age);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_value_int32 failed! status=%{public}d", status);
        return nullptr;
    }
    // 获取姓名参数
    status = napi_get_value_string_utf8(env, argv[1], buf, BUFF_SIZE, &len);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_value_string_utf8 failed! status=%{public}d", status);
        return nullptr;
    }
    // 创建person实例
    person *ps = new person(age, std::string(buf));
    
    // 绑定实例到对象并获取对象的生命周期
    status = napi_wrap(env, jsthis, ps, [](napi_env env, void* finalize_data, void* finalize_hint)
                                        {
                                            delete reinterpret_cast<person *>(finalize_data);
                                        }, nullptr, nullptr);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_wrap failed! status=%{public}d", status);
        return nullptr;
    }
    
    return jsthis;
}

napi_value person::Init(napi_env env, napi_value &exports)
{
    // 设置类对外导出的方法
    napi_property_descriptor descClass[] = {
        {"SayAge", nullptr, person::SayAge, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"SayName", nullptr, person::SayName, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"name", nullptr, nullptr, person::GetName, person::SetName, nullptr, napi_default, nullptr}
    };
    
    // 定义JS类
    napi_status status;
    napi_value cons;
    napi_ref *ref = new napi_ref;
    status = napi_define_class(env, "person", NAPI_AUTO_LENGTH, person::Constructor, nullptr, sizeof(descClass) / sizeof(descClass[0]), descClass, &cons);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_define_class falied, satus=%{public}d", status);
        return nullptr;
    }
    // 创建生命周期
    status = napi_create_reference(env, cons, 1, ref);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_reference falied, satus=%{public}d", status);
        return nullptr;
    }
    // 将类以属性值的方式导出
    status = napi_set_named_property(env, exports, "person", cons);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_set_named_property falied, satus=%{public}d", status);
        return nullptr;
    }
    
    // 保存生命周期变量
    napi_set_instance_data(env, ref, [](napi_env env, void *data, void *hint){
        uint32_t count = 0;
        napi_ref *ref = (napi_ref *)data;
        napi_reference_unref(env, *ref, &count);      // 程序结束后进入此回调，释放生命周期变量
        napi_delete_reference(env, *ref);
        delete ref;
    }, nullptr);
    
    return exports;
}

//返回类对象
napi_value GetPerson(napi_env env, napi_callback_info info) {
    napi_value constructs;
    napi_status status;
    napi_ref *ref;
    // 获取初始化时保存的生命周期
    status = napi_get_instance_data(env, (void **)&ref);
    // 获取生命周期保存的JS对象
    status = napi_get_reference_value(env, *ref, &constructs);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_get_reference_value falied, satus=%{public}d", status);
        return nullptr;
    }

    size_t argc = 2;
    napi_value argv[2];

    status = napi_create_int32(env, 18, &argv[0]);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_int32 falied, satus=%{public}d", status);
        return nullptr;
    }

    status = napi_create_string_utf8(env, "xiaoli", NAPI_AUTO_LENGTH, &argv[1]);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_string_utf8 falied, satus=%{public}d", status);
        return nullptr;
    }

    napi_value instance;
    // 创建新的JS对象(此时会触发person类的构造函数)
    status = napi_new_instance(env, constructs, argc, argv, &instance);
    if (status != napi_ok) {
        OH_LOG_INFO(LOG_APP, "napi_create_string_utf8 falied, satus=%{public}d", status);
        return nullptr;
    }
    
    person *ps;
    //从JS对象中获取C++实例对象
    status = napi_unwrap(env, instance, (void**)&ps);
    ps->name_ = "xiaoxiao";

    return instance;
}
